/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package opk.Solver;


import java.awt.Point;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashMap;
import opk.Generator.Edge;
import opk.Generator.Shape;

/**
 *
 * @author mikeqcp
 */
public class Preprocessor {

    private ArrayList<Shape>shapes;

    private ArrayList<Shape>shapes2;
    private HashMap<GraphPoint, HashMap<GraphPoint, ArrayList<Edge>>> allShortcuts;
    
    public Preprocessor(ArrayList<Shape> shapes)
    {
        this.shapes = (ArrayList<Shape>) shapes.clone();
        this.shapes2 = (ArrayList<Shape>) shapes.clone();
        allShortcuts = null;
    }

//    private Shape correctShape(Shape shape)
//    {
//        shape.makeEdgesList();
//        ArrayList<Edge> edges = shape.getEdgesList();
//
//        for (int i = 0; i < edges.size(); ++i)
//        {
//            if (edges.get(i).intersectsWithAnything(shapes2))
//            {
//                if (allShortcuts == null) setShortcuts();
//
//                GraphPoint graphPoint1 = edges.get(i).getGraphPoint1();
//                GraphPoint graphPoint2 = edges.get(i).getGraphPoint2();
//
////                HashMap<GraphPoint, ArrayList<Edge>> firstPointShortCuts = allShortcuts.get(graphPoint1);
////                ArrayList<Edge> edgesToAdd = firstPointShortCuts.get(graphPoint2);
//
//                if (edgesToAdd == null)
//                {
//                    System.out.println("");
//                }
//                edges.addAll(i, edgesToAdd);
//                i += edgesToAdd.size();
//                edges.remove(i);
//                --i;
//            }
//        }
//
//        shape.setEdgesList(edges);
//        return shape;
//    }

//    private void setShortcuts()
//    {
//        allShortcuts = (new DijkstraAlgorithm()).getAllShortcuts(shapes2);
//    }

    private Shape mergeTwo(Shape a, Shape b)
    {

        ArrayList<Shape> toMerge = new ArrayList<Shape>();
        toMerge.add(a);
        toMerge.add(b);


        Shape shape = (new GrahamAlgorithm().getShapeAfterGraham(toMerge));
//        correctShape(shape);

        return shape;
    }

    public ArrayList<Shape> getMergeaad()
    {
        ArrayList<Shape> result = new ArrayList<Shape>();
        Shape temp = new Shape();

        while(!shapes.isEmpty())
        {
            temp = new Shape(shapes.get(0));
            ArrayList<Shape> shapesToRemove = new ArrayList<Shape>();

           while(!findAllCollided(temp).isEmpty())
            {
               Shape s = findAllCollided(temp).get(0);
               
               shapes.remove(s);
               shapes.remove(temp);
               temp = mergeTwo(temp, s);
            }

            result.add(temp);
            shapes.removeAll(shapesToRemove);
            shapes.remove(shapes.get(0));
        }

        return result;
    }

    private ArrayList<Shape> findAllCollided(Shape a)
    {
        ArrayList<Shape> al = new ArrayList<Shape>();

        for(Shape s : shapes)
        {
            if(s!=a && (a.doesShapeCollide(s))) al.add(s);
        }

        return al;
    }

      public ArrayList<Shape> getMerged()
    {
       ArrayList<Shape> result = new ArrayList<Shape>();
       Shape toMergeA = null;
       Shape toMergeB = null;

 
        boolean again = true;
        while (again)
        {
            again = false;
            for (Shape s : shapes)
            {
                for (Shape candidate : shapes)
                {
                    if (s != candidate && s.doesShapeCollide(candidate))
                    {
                        toMergeA = s;
                        toMergeB = candidate;
                        again = true;
                        break;
                    }

                }
                if (again) break;
            }
            if (again)
            {
//                Shape newS = mergeTwo(toMergeA, toMergeB);
//                Shape newS = addShapes(toMergeA, toMergeB);
                Shape newS = mergeNodes(toMergeA, toMergeB);
                newS.setWeight(toMergeA.getWeight()+toMergeB.getWeight());
//                shapes.add(test(toMergeB));
//                shapes.add(test(toMergeA));
                shapes.remove(toMergeA);
                shapes.remove(toMergeB);
                shapes.add(newS);
            }

        }

        return shapes;
    }

    private Shape test(Shape a)
    {
        Area areaA = new Area(a.getPolygon());
        Shape tt = new Shape(areaA);
        return new Shape(areaA);
    }

      private Shape addShapes(Shape a, Shape b)
      {
          Area areaA = new Area(a.getPolygon());
          Area areaB = new Area(b.getPolygon());

          areaA.add(areaB);

          Shape result =  new Shape(areaA);
//          result.setWeight(a.getWeight() + b.getWeight());

          return new Shape(areaA);
      }

      private Shape mergeNodes(Shape a, Shape b)
      {

          ArrayList<Point> nodes = new ArrayList<Point>();
//          nodes.addAll(a.nodes);
//          nodes.addAll(b.nodes);

//          for(Point p : a.nodes)
//          {
//              if(b.isPointInside(p))
//                  nodes.remove(p);
//          }
//          for(Point p :b.nodes)
//          {
//              if(a.isPointInside(p))
//                  nodes.remove(p);
//          }

          Point cross = null;
          Edge edge1 = null, edge2 =null;
          for(Edge e : a.getEdgesList())
          {
              for(Edge e2 : b.getEdgesList())
              {
                  if(e.intersectsLine(e2))
                  {
                      cross = Intersects(e.getP1(), e.getP2(), e2.getP1(), e2.getP2());
                      edge1 = e;
                      edge2 = e2;
                  }
              }
          }

          int x, y;
          x = a.nodes.indexOf(edge1.getP1());
          y = a.nodes.indexOf(edge1.getP2());
          
          nodes.addAll(a.nodes);
          
          int marker;
                  
          if(x<y || y==0)
          {
             nodes.add(y, cross); 
             marker = y;
          } else
          {
              nodes.add(x, cross); 
              marker = x;
          }
  
          ArrayList<Point> temp = new ArrayList<Point>();

          x = b.nodes.indexOf(edge2.getP1());
          y = b.nodes.indexOf(edge2.getP2());
          int index;
          if(x<y || y==0) index = y;
            else index =x;

              for(int k=index; k<b.nodes.size(); k++)
              {
                  temp.add(b.nodes.get(k));
              }
              for(int k=0; k<index; k++)
              {
                  temp.add(b.nodes.get(k));
              }
          temp.add(cross);

          nodes.addAll(marker+1, temp);


          return new Shape(nodes);



      }


      private Point Intersects( Point2D line1Point1, Point2D line1Point2, Point2D line2Point1, Point2D line2Point2)
        {
          float q = (float) ((line1Point1.getY() - line2Point1.getY()) * (line2Point2.getX() - line2Point1.getX()) - (line1Point1.getX() - line2Point1.getX()) * (line2Point2.getY() - line2Point1.getY()));
          float d = (float) ((line1Point2.getX() - line1Point1.getX()) * (line2Point2.getY() - line2Point1.getY()) - (line1Point2.getY() - line1Point1.getY()) * (line2Point2.getX() - line2Point1.getX()));

          if( d == 0 )
          {
           return null;
          }

          float r = q / d;


          q = (float) ((line1Point1.getY() - line2Point1.getY()) * (line1Point2.getX() - line1Point1.getX()) - (line1Point1.getX() - line2Point1.getX()) * (line1Point2.getY() - line1Point1.getY()));
          float s = q / d;

          if( r < 0 || r > 1 || s < 0 || s > 1 )
          {
           return null;
          }

        /*
               Px=Ax+r(Bx-Ax)-
               Py=Ay+r(By-Ay)
        */

          Point intersection = new Point();

          intersection.x = (int) (line1Point1.getX() + (int) (0.5f + r * (line1Point2.getX() -
        line1Point1.getX())));
          intersection.y = (int) (line1Point1.getY() + (int) (0.5f + r * (line1Point2.getY() -
        line1Point1.getY())));
          return intersection;
        }

}
